
package com.gitee.jmash.rbac.service;

import com.gitee.jmash.common.tree.Tree;
import com.gitee.jmash.common.utils.UUIDUtil;
import com.gitee.jmash.core.orm.cdi.JpaTenantService;
import com.gitee.jmash.core.orm.jpa.TenantEntityManager;
import com.gitee.jmash.core.orm.tenant.TenantService;
import com.gitee.jmash.core.transaction.JakartaTransaction;
import com.gitee.jmash.rbac.dao.RoleDao;
import com.gitee.jmash.rbac.dao.RolesDutyDao;
import com.gitee.jmash.rbac.dao.RolesPermsDao;
import com.gitee.jmash.rbac.dao.UsersJobsDao;
import com.gitee.jmash.rbac.dao.UsersRolesDao;
import com.gitee.jmash.rbac.entity.RoleEntity;
import com.gitee.jmash.rbac.entity.RolesDutyEntity;
import com.gitee.jmash.rbac.model.TreeResult;
import jakarta.enterprise.inject.Typed;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import jakarta.transaction.Transactional.TxType;
import jakarta.validation.executable.ValidateOnExecution;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import jmash.rbac.protobuf.DutyType;
import jmash.rbac.protobuf.RoleReq;
import jmash.rbac.protobuf.VerifyRoleReq;

/**
 * 角色/职务表 rbac_role读服务.
 *
 * @author <a href="mailto:service@crenjoy.com">crenjoy</a>
 */
@Typed(RoleRead.class)
@Transactional(TxType.SUPPORTS)
@JpaTenantService
@ValidateOnExecution
public class RoleReadBean implements RoleRead, JakartaTransaction {

  protected TenantEntityManager tem = new TenantEntityManager();

  protected RoleDao roleDao = new RoleDao(this.tem);
  protected RolesPermsDao rolesPermsDao = new RolesPermsDao(this.tem);
  protected UsersRolesDao usersRolesDao = new UsersRolesDao(this.tem);
  protected RolesDutyDao dutyDao = new RolesDutyDao(this.tem);
  protected UsersJobsDao usersJobsDao = new UsersJobsDao(this.tem);

  @PersistenceContext(unitName = "ReadRbac")
  public void setEntityManager(EntityManager entityManager) {
    this.tem.setEntityManager(entityManager, false);
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T extends TenantService> T setTenant(String tenant) {
    this.tem.setTenant(tenant);
    return (T) this;
  }

  @Override
  public EntityManager getEntityManager() {
    return this.tem.getEntityManager();
  }

  @Override
  public void setTenantOnly(String tenant) {
    this.tem.setTenantOnly(tenant);
  }

  @Override
  public String getTenant() {
    return this.tem.getTenant();
  }

  @Override
  public RoleEntity findById(UUID entityId) {
    return roleDao.find(entityId);
  }

  @Override
  public List<RoleEntity> findListByReq(RoleReq req) {
    return roleDao.findListByReq(req);
  }

  @Override
  public Map<UUID, String> findUserRoleMap(UUID userId, String scope) {
    scope = null == scope ? "" : scope;
    Set<String> roleCodes = Arrays.asList(scope.split(",")).stream().collect(Collectors.toSet());
    return findUserRoleMap(userId, roleCodes);
  }

  @Override
  public Map<UUID, String> findUserRoleMap(UUID userId, Set<String> roleCodes) {
    // 用户授权角色ID
    Map<UUID, String> grantRolesId = findGrantRoles(userId).stream().collect(
        Collectors.toMap(RoleEntity::getRoleId, RoleEntity::getRoleCode, (key1, key2) -> key2));
    // 角色冲突,动态职责分离.
    List<RolesDutyEntity> dutys = dutyDao.findDutyList(DutyType.DSD);
    if (dutys.isEmpty()) {
      return grantRolesId;
    }
    // 动态职责冲突.
    for (RolesDutyEntity duty : dutys) {
      if (grantRolesId.containsKey(duty.getSrcRoleId())
          && grantRolesId.containsKey(duty.getDescRoleId())) {
        String srcRoleCode = grantRolesId.get(duty.getSrcRoleId());
        String descRoleCode = grantRolesId.get(duty.getDescRoleId());
        if (roleCodes.contains(srcRoleCode) && roleCodes.contains(descRoleCode)) {
          grantRolesId.remove(duty.getSrcRoleId());
          grantRolesId.remove(duty.getDescRoleId());
        } else if (roleCodes.contains(srcRoleCode)) {
          grantRolesId.remove(duty.getDescRoleId());
        } else if (roleCodes.contains(descRoleCode)) {
          grantRolesId.remove(duty.getSrcRoleId());
        } else {
          grantRolesId.remove(duty.getSrcRoleId());
          grantRolesId.remove(duty.getDescRoleId());
        }
      }
    }

    return grantRolesId;
  }

  @Override
  public List<TreeResult> findTreeList(RoleReq roleReq) {
    return roleDao.findTreeResultByReq(roleReq);
  }

  @Override
  public List<RolesDutyEntity> findDutyList(UUID roleId) {
    return dutyDao.findDutyList(roleId);
  }

  /** 获取用户授予的角色列表. */
  protected List<RoleEntity> findGrantRoles(UUID userId) {
    List<UUID> list = usersRolesDao.findUserRoles(userId);
    list.addAll(usersJobsDao.findUserRoles(userId));
    if (list.isEmpty()) {
      return Collections.emptyList();
    }
    Tree<RoleEntity, UUID> tree = findTree();
    List<RoleEntity> grantRoles = new ArrayList<>();
    for (UUID roleId : list) {
      grantRoles.addAll(tree.getSelfParentsList(roleId));
    }
    return grantRoles;
  }

  /** 构建角色内存树. */
  protected Tree<RoleEntity, UUID> findTree() {
    Tree<RoleEntity, UUID> tree = new Tree<RoleEntity, UUID>();
    List<RoleEntity> list =
        roleDao.findListByReq(RoleReq.newBuilder().setHasRoleType(false).build());
    if (list.size() == 0) {
      return tree;
    }
    for (RoleEntity entity : list) {
      tree.add(entity.getRoleId(), entity, entity.getParentId(), entity.getOrderBy());
    }
    return tree;
  }

  /** 校验角色编码是否存在. */
  @Override
  public boolean checkRoleCode(VerifyRoleReq req) {
    RoleEntity roleEntity = roleDao.findByCode(req.getRoleCode());
    if (roleEntity != null) {
      String roleId = req.getRoleId();
      return !roleEntity.getRoleId().equals(UUIDUtil.fromString(roleId));
    } else {
      return false;
    }
  }

  @Override
  public boolean checkSystemRole(String directoryId, String userName) {
    return roleDao.checkSystemRole(directoryId, userName);
  }

  @Override
  public List<RoleEntity> getDsdRoleList() {
    return roleDao.getDsdRoleList();
  }

  @Override
  public void close() throws Exception {
    CDI.current().destroy(this);
  }

}
